#include "WebsocketHandler.h"
#include "WWebsocketServer.h"

WebsocketHandler::WebsocketHandler()
{
    _websocketServer = new WWebsocketServer();
    WsCbFun msgCbFun = std::bind(&WebsocketHandler::onWsReceiveMsg, this, std::placeholders::_1);
    _websocketServer->setWsMsgCbFun(msgCbFun);
    WsCbFun connectCbFun = std::bind(&WebsocketHandler::onWsConnect, this, std::placeholders::_1);
    _websocketServer->setWsConnectCbFun(connectCbFun);
    WsCbFun closeCbFun = std::bind(&WebsocketHandler::onWsCloseMsg, this, std::placeholders::_1);
    _websocketServer->setWsCloseCbFun(closeCbFun);
}

WebsocketHandler::~WebsocketHandler()
{
    delete _websocketServer;
}

void WebsocketHandler::onWsReceiveMsg(shared_ptr<WsRequestMsg> &wsMsg)
{
    Logi("WebsockectHandler::onWsReceiveMsg recevice ws msg: %s", wsMsg->receiveMsg.c_str());
    SignalOrderInfo orderInfo = parseOrderInfo(wsMsg);
    if (orderInfo.orderType == "join")
    {
        handleJoinOrder(wsMsg, orderInfo);
    }
    else if (orderInfo.orderType == "answerOffer")
    {
        handleAnswerOfferOrder(wsMsg, orderInfo);
    }
    else if (orderInfo.orderType == "candidate")
    {
        handleIceCandiateOrder(wsMsg, orderInfo);
    }
}

void WebsocketHandler::onWsConnect(shared_ptr<WsRequestMsg> &)
{

}

void WebsocketHandler::onWsCloseMsg(shared_ptr<WsRequestMsg> &wsMsg)
{
    unique_lock<mutex> locker(_orderMutex);
    int64_t fd = (int64_t)wsMsg->wsConnection->fd;
    if (_fdToPeerInfo.find(fd) != _fdToPeerInfo.end())
    {
        string roomId = _fdToPeerInfo[fd].roomId;
        list<PeerInfo> &peerInfoList = _roomToPeerListMap[roomId];
        for(auto it = peerInfoList.begin(); it != peerInfoList.end(); it++)
        {
            if (it->fd == fd)
            {
                peerInfoList.erase(it);
                break;
            }
        }
        if (peerInfoList.empty())
        {
            _roomToPeerListMap.erase(roomId);
        }
        _fdToPeerInfo.erase(fd);
    }
}

bool WebsocketHandler::start()
{
    return _websocketServer->startWsServer(6310);
}

void WebsocketHandler::handleRecvRtp(shared_ptr<WsRequestMsg> wsRequest, char *data, int len)
{
    list<PeerInfo> peerInfoList = getPeerInfoList((int64_t)wsRequest->wsConnection->fd);
    for (auto it = peerInfoList.begin(); it != peerInfoList.end(); it++)
    {
//        cout << "get here to send data " << it->userName << endl;
        rtp_header* rtp_hdr = (rtp_header*)data;
        rtp_hdr->ssrc        = htonl(12345678);
        it->iceAgent->sendDataNeedProtect(1, 1, len, data);
    }
}

void WebsocketHandler::handleJoinOrder(shared_ptr<WsRequestMsg> &wsMsg, SignalOrderInfo &orderInfo)
{
    // _fdToRoomMap:fd -> peerInfo,  _roomToOrderInfoMap: roomId -> 人员信息列表list<PeerInfo>
    unique_lock<mutex> locker(_orderMutex);
    if (_fdToPeerInfo.find(orderInfo.fd) != _fdToPeerInfo.end())
    {
        Logi("this peer is already in room:%s", orderInfo.userName.c_str());
        return;
    }

    PeerInfo peerInfo;
    transOrderInfoToPeerInfo(orderInfo, peerInfo);
    peerInfo.iceAgent = shared_ptr<WIceAgent>(new WIceAgent(_websocketServer, wsMsg, true));
    peerInfo.iceAgent->setIceRecvCb(this);
    _fdToPeerInfo[orderInfo.fd] = peerInfo;
    if (_roomToPeerListMap.find(orderInfo.roomId) == _roomToPeerListMap.end())
    {
       list<PeerInfo> peerInfoList;
       peerInfoList.push_back(peerInfo);
       _roomToPeerListMap[orderInfo.roomId] = peerInfoList;
    }
    else
    {
        list<PeerInfo> &peerInfoList = _roomToPeerListMap[orderInfo.roomId];
        peerInfoList.push_back(peerInfo);
    }
    SignalOrderInfo repOrderInfo;
    repOrderInfo.orderType = "joined";
    _websocketServer->sendMsg(wsMsg, repOrderInfo.toJson());

    int nstreamid = peerInfo.iceAgent->addStream("video", 1);
    peerInfo.iceAgent->startGather(nstreamid);
}

void WebsocketHandler::handleAnswerOfferOrder(shared_ptr<WsRequestMsg> &wsMsg, SignalOrderInfo &orderInfo)
{
    // Logi("WebsockectHandler::handleAnswerOfferOrder enter");
    PeerInfo peerInfo = getPeerInfo((int64_t)wsMsg->wsConnection->fd);
    peerInfo.iceAgent->setRemoteSdp(orderInfo.sdpInfo.sdp.c_str());
}

void WebsocketHandler::handleIceCandiateOrder(shared_ptr<WsRequestMsg> &wsMsg, SignalOrderInfo &orderInfo)
{
    PeerInfo peerInfo = getPeerInfo((int64_t)wsMsg->wsConnection->fd);
    Logi("WebsockectHandler::handleIceCandiateOrder recv candidate is:%s", orderInfo.candidate.c_str());
    peerInfo.iceAgent->setRemoteCandidate(orderInfo.candidate.c_str());
}

SignalOrderInfo WebsocketHandler::parseOrderInfo(shared_ptr<WsRequestMsg> wsMsg)
{
    SignalOrderInfo res;
    CJsonObject jsonObj;
    if (jsonObj.Parse(wsMsg->receiveMsg))
    {
        if (jsonObj.KeyExist("orderType"))
        {
            jsonObj.Get("orderType", res.orderType);
        }

        jsonObj.Get("roomId", res.roomId);
        jsonObj.Get("userId", res.userId);
        jsonObj.Get("userName", res.userName);
        res.fd  = (int64_t)wsMsg->wsConnection->fd;

        if (res.orderType == "answerOffer")
        {
            CJsonObject msgObject;
            jsonObj.Get("msg", msgObject);

            msgObject.Get("type", res.sdpInfo.type);
            msgObject.Get("sdp", res.sdpInfo.sdp);
        }
        else if (res.orderType == "candidate")
        {
            jsonObj.Get("msg", res.candidate);
        }
    }
    else
    {
        Logw("WebsockectHandler::parseOrderInfo parse json error");
    }

    return res;
}

PeerInfo WebsocketHandler::getPeerInfo(int64_t fd)
{
    unique_lock<mutex> locker(_orderMutex);
    if (_fdToPeerInfo.find(fd) == _fdToPeerInfo.end())
    {
        Logw("WebsockectHandler::getPeerInfo can not find this fd:%ld", fd);
        return PeerInfo();
    }
    return _fdToPeerInfo[fd];
}

list<PeerInfo> WebsocketHandler::getPeerInfoList(int64_t fd)
{
    unique_lock<mutex> locker(_orderMutex);
    if (_fdToPeerInfo.find(fd) == _fdToPeerInfo.end())
    {
        Logw("WebsockectHandler::getPeerInfoList can not find this fd:%ld", fd);
        return list<PeerInfo>();
    }
    PeerInfo tempPeer = _fdToPeerInfo[fd];
    return _roomToPeerListMap[tempPeer.roomId];
}

void WebsocketHandler::transOrderInfoToPeerInfo(SignalOrderInfo &orderInfo, PeerInfo &peerInfo)
{
    peerInfo.roomId = orderInfo.roomId;
    peerInfo.userId = orderInfo.userId;
    peerInfo.userName = orderInfo.userName;
    peerInfo.fd = orderInfo.fd;
}
